#include <StdAfx.h>
#include "FileIOWrapper.h"
#include <IDiskProfiler.h>
#include <StringUtils.h>
#include "FileReadSequencer.h"
#include "System.h"

//////////////////////////////////////////////////////////////////////////

#define g_szTestResults "TestResults"

#ifdef ENABLE_FILE_ACCESS_LOGGER
const char * strIOOperation[] =
{
	"OPEN",
	"READ",
	"SEEK",
	"TELL",
	"CLOSE",
	"OPENINPAK",
	"READINPAK",
	"SEEKINPAK",
	"CLOSEINPAK"
};

FileAccessLogger::FileAccessLogger(CIOWrapper::IOOperation action, FILE * file, size_t size, int64 offset,void *pInternalFile)
: m_Action(action), m_pFile(file), m_nSize(size), m_nOffset(offset), m_pInternalFile(pInternalFile)
{
	if (true) {
		m_fTime = gEnv->pTimer->GetAsyncTime();
		m_strFileName = 0;
	}
}

FileAccessLogger::FileAccessLogger(CIOWrapper::IOOperation action, const char *strName, FILE * file, size_t size)
: m_Action(action), m_pFile(file), m_nSize(size), m_nOffset(0), m_strFileName(strName),m_pInternalFile(0)
{
	if (true) {
		m_fTime = gEnv->pTimer->GetAsyncTime();
	}
}

FILE* FileAccessLogger::GetFile()
{
	static bool init = true;
	char path[ICryPak::g_nMaxPath];
	path[sizeof(path) - 1] = 0;



	gEnv->pCryPak->AdjustFileName(string(string(g_szTestResults) + "\\FileAccessLog.txt").c_str(), path, ICryPak::FLAGS_PATH_REAL | ICryPak::FLAGS_FOR_WRITING);


	FILE * f;
	if (gEnv->pCryPak && init) 
	{








		gEnv->pCryPak->MakeDir( g_szTestResults );
		f = ::fopen(path,"wt" );

		init = false;
		if(f)
		{
			fprintf(f, "TIME,                OPERATION,      SIZE,OFFSET(KB), TIME(ms),  FILE\n");
		}
	}
	else
	{



		f = ::fopen(path,"a" );

		CTimeValue currenttime = gEnv->pTimer->GetAsyncTime();
	}

	return f;
}

//////////////////////////////////////////////////////////////////////////
FileAccessLogger::~FileAccessLogger()
{
	if (g_cvars.pakVars.nFileAccessLog)
	{
		static CTimeValue lasttime = gEnv->pTimer->GetAsyncTime();
		m_fTime = gEnv->pTimer->GetAsyncTime() - m_fTime;
		FILE * f = GetFile();
		if (f) {
			CryFixedStringT<128> timeStr;
			CTimeValue currenttime = gEnv->pTimer->GetAsyncTime();
			uint32 dwMs=(uint32)((currenttime-lasttime).GetMilliSeconds());
			uint32 minutes = (dwMs / 60000) % 60;
			uint32 hours = minutes / 60;
			timeStr.Format( "[%3d:%2d:%2d:%.3d]", hours, minutes, (dwMs/1000) % 60,dwMs%1000 );

			//fprintf(f, "[%s] %s %s hFile=%i Size=%i OperationTime=%f\n", timeStr.c_str(), m_strAction, m_strFileName, m_pFile, m_nSize, m_fTime);
			// time, operation, LBA, Count, Read (ms), Seek(ms), Actual(ms), Emulator delay(ms), Files...

			CryAutoCriticalSection lock(m_HandleMapLock);

			const char * fileName = m_strFileName;

			if (!fileName)
			{
				static string sUnknown = "unknown";
				fileName = stl::find_in_map( m_HandleMap,m_pFile,sUnknown ).c_str();
			} else
			{
				if (m_pFile)
				{
					m_HandleMap[m_pFile] = m_strFileName;
				}
			}

			const char *internalName = "";
			if (m_pInternalFile)
			{
				static string sUnknown = "unknown";
				internalName = stl::find_in_map( m_HandleMap,m_pInternalFile,sUnknown ).c_str();
			}

			float msTime = m_fTime.GetMilliSeconds();
			int64 offset = m_nOffset;

			if (g_cvars.pakVars.nFileAccessLog == 2 ||
				(g_cvars.pakVars.nFileAccessLog == 1 && m_Action == CIOWrapper::IO_READ))
			{
				fprintf(f, "%16s, %12s, %10i, %10lli, %6.2f, %s %s\n", timeStr.c_str(), strIOOperation[m_Action], (int32)m_nSize, (long long)offset, msTime, fileName, internalName);
			}

			fclose(f);
		}
	}
}

void FileAccessLogger::RegisterFileHandle( void *pFileHandle,const char *name )
{
	if (g_cvars.pakVars.nFileAccessLog != 0 && (m_HandleMap.find(pFileHandle) == m_HandleMap.end()))
	{
		CryAutoCriticalSection lock(m_HandleMapLock);
		m_HandleMap[pFileHandle] = name;
	}
}

void FileAccessLogger::AddLabel(const char * label)
{
	FILE * f = GetFile();
	if (f) {
		fprintf(f,"==========================%s===================================,,,,,\n", label);
		fclose(f);
	}
}

FileAccessLogger::THandleMap	FileAccessLogger::m_HandleMap;
CryCriticalSection FileAccessLogger::m_HandleMapLock;

#endif // ENABLE_FILE_ACCESS_LOGGER

void CIOWrapper::SetLoggerState(int b)
{
	g_cvars.pakVars.nFileAccessLog = b;
}

//////////////////////////////////////////////////////////////////////////
size_t CIOWrapper::Fread(void *pData, size_t nSize, size_t nCount, FILE *hFile,void *pInternalFile) {

	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_SYSTEM );

	LOADING_TIME_PROFILE_SECTION;

	PROFILE_DISK_READ(nSize*nCount);

	int64 nReadOffset = m_nFileCursorVirtual - m_nFileCursorReal;



















	FileAccessLogger log(CIOWrapper::IO_READ, hFile, nSize*nCount, (int)(nReadOffset/1024),pInternalFile);

	//////////////////////////////////////////////////////////////////////////
	// Detect if seek probably needed.
	//////////////////////////////////////////////////////////////////////////
	if (m_nFileCursorReal != m_nFileCursorVirtual || m_pLastFile != hFile)
	{
		PROFILE_DISK_SEEK
	}
	m_nFileCursorVirtual += nSize*nCount;
	m_nFileCursorReal = m_nFileCursorVirtual;
	m_pLastFile = hFile;
	//////////////////////////////////////////////////////////////////////////

	if(CFileReadSequencer::m_nSequencerMode || CFileReadSequencer::m_nSequencerProfile>0)
		return CFileReadSequencer::Fread(pData, nSize, nCount, hFile);

	size_t nReadCount = 0;
	if (m_bLockReads)
	{
		AUTO_LOCK_CS(m_ReadCS);
		nReadCount = ::fread(pData, nSize, nCount, hFile);  
	} else
	{
		nReadCount = ::fread(pData, nSize, nCount, hFile);  
	}
	return nReadCount;
}




FILE * CIOWrapper::Fopen(const char * file, const char * mode)

{
	LOADING_TIME_PROFILE_SECTION;

	FileAccessLogger log(CIOWrapper::IO_OPEN, file, 0, 0);



#if (defined(WIN32) || defined(WIN64)) && !defined(gringo)
	FILE * f = ::_wfopen(CryStringUtils::UTF8ToWStr(file), CryStringUtils::UTF8ToWStr(mode));
#else
	FILE * f = ::fopen(file, mode);
#endif

	log.m_pFile = f;

	if(f)
	{
		FileAccessLogger::RegisterFileHandle(f,file);

		CFileReadSequencer::OnFopen(f, file, mode);
	}

	m_nFileCursorReal = m_nFileCursorVirtual = 0;
	m_pLastFile = 0;

	return f;
}

//////////////////////////////////////////////////////////////////////////
int CIOWrapper::Fclose(FILE * hFile)
{
	//FileAccessLogger log("fclose", hFile, 0, m_bLoggerEnabled);
	m_nFileCursorReal = m_nFileCursorVirtual = 0;
	m_pLastFile = 0;

	if(CFileReadSequencer::IsProfiling())
		return 0;

	return fclose(hFile);
}

//////////////////////////////////////////////////////////////////////////
long CIOWrapper::FTell(FILE * hFile)
{
	FileAccessLogger log(CIOWrapper::IO_TELL, hFile, 0,0,0);
	return ftell(hFile);
}

//////////////////////////////////////////////////////////////////////////
int CIOWrapper::Fseek(FILE * hFile, int64 seek, int mode,void *pInternalFile)
{
	FileAccessLogger log(CIOWrapper::IO_SEEK, hFile, 0,seek, pInternalFile);
	int nResult = fseek(hFile, (long)seek, mode);
	switch (mode)
	{
	case SEEK_SET:
		m_nFileCursorVirtual = seek;
		break;
	case SEEK_CUR:
		if (m_pLastFile == hFile)
			m_nFileCursorVirtual += seek;
		else
			m_nFileCursorVirtual = (uint64)ftell(hFile);
		break;
	case SEEK_END:
		// No need to track...
		break;
	}

	return nResult;
}

void CIOWrapper::AddLabel(const char * label) 
{
	if (g_cvars.pakVars.nFileAccessLog)
	{
		FileAccessLogger::AddLabel(label);
	}
}

FILE* CIOWrapper::m_pLastFile = 0;
int64 CIOWrapper::m_nFileCursorReal = 0;
int64 CIOWrapper::m_nFileCursorVirtual = 0;

bool CIOWrapper::m_bLockReads = false;
CryCriticalSection CIOWrapper::m_ReadCS;
